<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .helper {
            position: absolute;
            top: 0;
            left: 0;
            width: 300px;
            height: 100%;
            background-color: rgba(155, 155, 155, 1);
        }
    </style>

</head>

<body>
    <!-- 将按钮移到helper div外部 -->
    <button id="toggleHelperBtn"
        style="position: fixed; top: 10px; left: 10px; z-index: 1002; background-color: #f0f0f0; border: none; border-radius: 4px; padding: 4px 8px; cursor: pointer; display: none;">
        显示说明
    </button>
    <!-- 在屏幕的左上角，添加一个200*200的div -->
    <div class="helper">

        <!-- 原来的按钮位置可以保留一个占位符或删除 -->
        <div style="position: absolute; top: 10px; right: 10px; z-index: 1001;">
            <button id="helperCloseBtn"
                style="background-color: #f0f0f0; border: none; border-radius: 4px; padding: 4px 8px; cursor: pointer;">
                隐藏
            </button>
        </div>
        <h4>操作说明</h4>
        <ul>
            <li><b>模型操作</b>
                <ol>
                    <li>左键旋转模型</li>
                    <li>右键平移模型</li>
                </ol>
            </li>
            <li><b>模型显示和隐藏</b>
                <ol>
                    <li>在【模型显示】菜单点击按钮可以切换模型的显示和隐藏</li>
                </ol>
            </li>
            <li><b>方向显示</b>
                <ol>
                    <li>在【方向显示】菜单点击按钮可以切换x/y/z的显示和隐藏</li>
                </ol>
            </li>
            <li>
                <b>比例调节</b>
                <ol>
                    <li>在【比例调节】菜单，可以条件偏差箭头的显示大小</li>
                </ol>
            </li>
            <li>
                <b>显示偏差值</b>
                <ol>
                    <li>在【显示偏差值】偏差阈值，当小于该值时，不显示箭头</li>
                </ol>
            </li>
        </ul>
    </div>

    <div id="notifyToast"
        style="display: none; position: fixed; top: 20px; left: 50%; transform: translateX(-50%); padding: 12px 20px; background: #333; color: #fff; border-radius: 4px;">
    </div>
    <div id="targetControls"
        style="position: fixed; top: 60px; left: 50%; transform: translateX(-50%); z-index: 1000; display: flex; gap: 10px;">
        <p id="targetInfo" style="color: #fff;"> 旋转中心切换 </p>
        <button id="btnDefaultTarget"
            style="padding: 8px 16px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer;">
            默认中心 (1800,0,600)
        </button>
        <button id="btnLastClickTarget"
            style="padding: 8px 16px; background-color: #2196F3; color: white; border: none; border-radius: 4px; cursor: pointer;">
            当前测点位置
        </button>
    </div>

    <script type="module">
        import { utils_readCSV } from './utils/readcsv.ts';
        import { utils_loadgltf, lazyLoadModel, loadedModels, modelsToLoad } from './utils/load_gltf.ts';
        import { request, request_coorddata, g_last_vin_list, CACHE_KEY_VIN_LIST, getCachedDataByVin } from './utils/request.ts';
        // 轨道控制器
        import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
        import * as THREE from 'three';
        import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
        import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';
        // import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry.js';
        // 引入fonts
        // import { FontLoader } from 'three/examples/jsm/loaders/FontLoader.js';


        // 从环境变量中获取VITE_CHEXING
        const chexing = import.meta.env.VITE_CHEXING
        const gui = new GUI();
        // 初始化场景
        // if (true) {

        // 创建相机
        const camera = new THREE.PerspectiveCamera(90, window.innerWidth / window.innerHeight, 0.1, 8000);
        camera.position.set(0, -4000, 0)
        camera.lookAt(1800, 0, 600);


        // 创建渲染器
        const renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);
        // 添加轨道控制器（可选，方便交互）
        const controls = new OrbitControls(camera, renderer.domElement);
        controls.enableDamping = true;
        controls.dampingFactor = 0.05;
        // 配置target
        controls.target.set(1800, 0, 600);
        // 平移配置
        controls.enablePan = true; // 启用平移
        controls.panSpeed = 1.0; // 平移速度
        controls.screenSpacePanning = true; // 屏幕空间平移
        // 如果设为false，则使用轨道球平移（基于球体表面）


        // 鼠标按钮配置
        controls.mouseButtons = {
            LEFT: THREE.MOUSE.ROTATE,
            MIDDLE: THREE.MOUSE.DOLLY,
            RIGHT: THREE.MOUSE.PAN
        };

        // 创建场景
        const scene = new THREE.Scene();
        scene.background = new THREE.Color(0x000000);


        // 添加环境光
        // const ambientLight = new THREE.AmbientLight(0xffffff, 4);
        // scene.add(ambientLight);

        // 添加平行光
        const directionalLight1 = new THREE.DirectionalLight(0xffffff, 0.5);
        directionalLight1.position.set(5, -10, 7.5);
        scene.add(directionalLight1);


        const directionalLight2 = new THREE.DirectionalLight(0xffffff, 0.5);
        directionalLight2.position.set(-5, 10, 7.5);
        scene.add(directionalLight2);

        const gridHelper = new THREE.GridHelper(100);
        scene.add(gridHelper);
        // }

        // 加载所有模型
        // const g_models = await utils_loadAllModels(scene);


        // 存储模型引用的对象

        // 
        const gui_model = gui.addFolder('模型显示');
        // gui_model 默认折叠
        // gui_model.close()
        // modelsToLoad 的每个键作为 gui_model_obj 键，属性都是false
        const gui_model_obj = {}
        for (const modelName of Object.keys(modelsToLoad)) {
            gui_model_obj[modelName] = false;
        }

        // 修改GUI模型显示控制
        for (const modelName of Object.keys(modelsToLoad)) {
            gui_model.add(gui_model_obj, modelName).name(modelName).onChange(async (value) => {
                if (value) {
                    // 需要显示时才加载模型
                    const model = await lazyLoadModel(modelName, scene);
                    if (model) {
                        model.visible = true;
                    }
                } else if (loadedModels[modelName]) {
                    // 已加载的模型直接隐藏
                    loadedModels[modelName].visible = false;
                }
            });
        }



        // 方向显示交互gui
        const gui_direction = gui.addFolder('方向显示');
        const gui_dir_obj = {
            'x': true,
            'y': true,
            'z': true,
            'sphere': false,
        }
        gui_direction.add(gui_dir_obj, 'x').name('x轴').onChange((value) => {
            for (const key in g_points3d) {
                // 改变x轴的箭头
                g_points3d[key].x.visible = value;
            }
        })
        gui_direction.add(gui_dir_obj, 'y').name('y轴').onChange((value) => {
            for (const key in g_points3d) {
                // 改变y轴的箭头
                g_points3d[key].y.visible = value;
            }
        })
        gui_direction.add(gui_dir_obj, 'z').name('z轴').onChange((value) => {
            for (const key in g_points3d) {
                // 改变z轴的箭头
                g_points3d[key].z.visible = value;
            }
        })
        gui_direction.add(gui_dir_obj, 'sphere').name('球体').onChange((value) => {
            for (const key in g_points3d) {
                // 改变球体的显示
                g_points3d[key].sphere.visible = value;
            }
        })


        const gui_bili = gui.addFolder('比例调节');
        const gui_bili_obj = {
            arrow: 1,
            sphere: 1,
        }
        gui_bili.add(gui_bili_obj, 'arrow', 0.2, 5, 0.2).name('箭头比例').onChange((value) => {
            for (const key in g_points3d) {
                // 改变箭头的比例
                g_points3d[key].x.scale.set(value, value, value);
                g_points3d[key].z.scale.set(value, value, value);
                g_points3d[key].y.scale.set(value, value, value);
            }
        })
        gui_bili.add(gui_bili_obj, 'sphere', 0.2, 5, 0.2).name('球体比例').onChange((value) => {
            for (const key in g_points3d) {
                // 改变球体的比例
                g_points3d[key].sphere.scale.set(value, value, value);
            }
        })


        const gui_xianshiyuzhi = gui.addFolder('显示偏差值');
        const gui_xianshiyuzhi_obj = {
            x: 0.5,
        }
        gui_xianshiyuzhi.add(gui_xianshiyuzhi_obj, 'x', 0.1, 5, 0.1).name('偏差阈值').onChange((value) => {
            for (const key in g_points3d) {
                if (gui_dir_obj.x) {
                    if (Math.abs(g_coorddataobj[key].last.x) > value) {
                        g_points3d[key].x.visible = true;
                    } else {
                        g_points3d[key].x.visible = false;
                    }
                }
                if (gui_dir_obj.y) {
                    if (Math.abs(g_coorddataobj[key].last.y) > value) {
                        g_points3d[key].y.visible = true;
                    } else {
                        g_points3d[key].y.visible = false;
                    }
                }
                if (gui_dir_obj.z) {
                    if (Math.abs(g_coorddataobj[key].last.z) > value) {
                        g_points3d[key].z.visible = true;
                    } else {
                        g_points3d[key].z.visible = false;
                    }
                }
            }
        })

        const gui_last = gui.addFolder('最后100台选择');



        const gui_last_obj = {
            last: '最新1台数据'
        }
        // 生成一个从1到100的列表
        gui_last.add(gui_last_obj, 'last', g_last_vin_list).name('最后第XX台').onChange((value) => {
            console.log('value:', value)
            update_points3d(value)
        })


        //////////////
        const gui_cankaoduibi = gui.addFolder('显示相对偏差');
        const gui_cankaoduibi_obj = {
            enable: true,
            cached_vin: '------'
        }
        gui_cankaoduibi.add(gui_cankaoduibi_obj, 'enable').name('显示相对偏差').onChange((value) => {
            console.log('gui_cankaoduibi_obj.enable:', value)
        })


        gui_cankaoduibi.add(gui_cankaoduibi_obj, 'cached_vin', CACHE_KEY_VIN_LIST).name('参考对比VIN').onChange(async (vin) => {
            // 先判断是否开启了显示相对偏差
            if (!gui_cankaoduibi_obj.enable) {
                return
            }
            // console.log('vin:', vin)
            // 获取到参考对比的数据obj
            // let cankao_last_i = value
            // let coorddataobj_cankao = await request_coorddata(cankao_last_i)
            let coorddataobj_cankao = getCachedDataByVin(vin)

            console.log('coorddataobj_cankao:', coorddataobj_cankao)
            // console.log('coorddataobj 减之前:', g_coorddataobj)
            // 将coorddataobj.last 每个x,y,z 减去cankao_last_i的x,y,z
            for (const key in g_coorddataobj) {
                if (key in coorddataobj_cankao) {
                    // console.log('key:', key)
                    // console.log('g_coorddataobj[key].last:', g_coorddataobj[key].last)
                    // console.log('coorddataobj_cankao[key].last:', coorddataobj_cankao[key].last)
                    coorddataobj_cankao[key].last.x -= g_coorddataobj[key].last.x
                    coorddataobj_cankao[key].last.x = -coorddataobj_cankao[key].last.x
                    coorddataobj_cankao[key].last.y -= g_coorddataobj[key].last.y
                    coorddataobj_cankao[key].last.y = -coorddataobj_cankao[key].last.y
                    coorddataobj_cankao[key].last.z -= g_coorddataobj[key].last.z
                    coorddataobj_cankao[key].last.z = -coorddataobj_cankao[key].last.z
                    // console.log('coorddataobj_cankao[key].last 减之后:', coorddataobj_cankao[key].last)
                }
            }
            // console.log('coorddataobj 减之后:', g_coorddataobj)


            // 更新偏差值
            update_points3d_from_coord(coorddataobj_cankao)
        })

        /////
        const gui_region = gui.addFolder('显示区域');
        const gui_region_obj = {
            jicang: true,
            qiandiban: true,
            houcheti: true,
            cewei: true,
            dinggai: true,
            L: true,
            R: true,
            K: true,
            G_E: true,
        }

        function set_visible(key, value) {
            if (key in g_points3d) {
                g_points3d[key].x.visible = value
                g_points3d[key].y.visible = value
                g_points3d[key].z.visible = value
                // g_points3d[key].sphere.visible = value
            }
        }

        gui_region.add(gui_region_obj, 'jicang').name('显示机舱').onChange((value) => {
            console.log('gui_region_obj.jicang:', value)
            for (const key in g_points3d) {
                // key 的第3个字符是否为0
                if (key.charAt(2) === '0') {
                    // console.log('key:', key, key.charAt(2))
                    set_visible(key, value)
                }
            }
        })
        gui_region.add(gui_region_obj, 'qiandiban').name('显示前地板').onChange((value) => {
            console.log('gui_region_obj.qiandiban:', value)
            for (const key in g_points3d) {
                // key 的第3个字符是否为1
                if (key.charAt(2) === '1') {
                    // console.log('key:', key, key.charAt(2))
                    set_visible(key, value)
                }
            }
        })
        gui_region.add(gui_region_obj, 'houcheti').name('显示后地板').onChange((value) => {
            console.log('gui_region_obj.houcheti:', value)
            for (const key in g_points3d) {
                // key 的第3个字符是否为2
                if (key.charAt(2) === '2') {
                    // console.log('key:', key, key.charAt(2))
                    set_visible(key, value)
                }
            }
        })
        gui_region.add(gui_region_obj, 'cewei').name('显示侧围').onChange((value) => {
            console.log('gui_region_obj.cewei:', value)
            for (const key in g_points3d) {
                // key 的第3个字符是否为3
                if (key.charAt(2) === '3') {
                    // console.log('key:', key, key.charAt(2))
                    set_visible(key, value)
                }
            }
        })
        gui_region.add(gui_region_obj, 'dinggai').name('显示顶盖').onChange((value) => {
            console.log('gui_region_obj.dinggai:', value)
            for (const key in g_points3d) {
                // key 的第3个字符是否为4
                if (key.charAt(2) === '4') {
                    // console.log('key:', key, key.charAt(2))
                    set_visible(key, value)
                }
            }
        })
        gui_region.add(gui_region_obj, 'L').name('显示左侧').onChange((value) => {
            console.log('gui_region_obj.L:', value)
            for (const key in g_points3d) {
                // key 的第7个字符是否为5
                if (key.charAt(6) === 'L') {
                    // console.log('key:', key, key.charAt(6))
                    set_visible(key, value)
                }
            }
        })
        gui_region.add(gui_region_obj, 'R').name('显示右侧').onChange((value) => {
            console.log('gui_region_obj.R:', value)
            for (const key in g_points3d) {
                // key 的第7个字符是否为6
                if (key.charAt(6) === 'R') {
                    // console.log('key:', key, key.charAt(6))
                    set_visible(key, value)
                }
            }
        })

        gui_region.add(gui_region_obj, 'K').name('显示关键点').onChange((value) => {
            console.log('gui_region_obj.K:', value)
            for (const key in g_points3d) {
                // key 的第1个字符是否为K
                if (key.charAt(0) === 'K') {
                    // console.log('key:', key, key.charAt(0))
                    set_visible(key, value)
                }
            }
        })
        gui_region.add(gui_region_obj, 'G_E').name('显示非关键点').onChange((value) => {
            console.log('gui_region_obj.G_E:', value)
            for (const key in g_points3d) {
                // key 的第1个字符是否为非K
                if (key.charAt(0) !== 'K') {
                    // console.log('key:', key, key.charAt(0))
                    set_visible(key, value)
                }
            }
        })

        // 用THREE.Points绘制3d点
        let g_points3d = {}

        // 导入3d点数据
        let g_coorddataobj;
        g_coorddataobj = await request_coorddata()
        console.log('coorddataobj:', g_coorddataobj)


        draw_points3d(g_coorddataobj)


        function animate() {
            requestAnimationFrame(animate);
            renderer.render(scene, camera);
        }
        animate();

        const raycaster = new THREE.Raycaster();
        let g_last_click_sphere = null;
        window.addEventListener('mousedown', (event) => {
            // console.log(event);
            // 转换为归一化设备坐标
            const mouse = new THREE.Vector2(
                (event.clientX / window.innerWidth) * 2 - 1,
                -(event.clientY / window.innerHeight) * 2 + 1
            );
            // 设置射线的起点为相机位置
            raycaster.setFromCamera(mouse, camera);
            // 计算射线与场景中的所有物体的交点
            // const intersects = raycaster.intersectObjects(scene.children);
            // console.log('intersects:', intersects);
            // console.log('points3d:', g_points3d)
            // 遍历points3d中的所有key，并将每个key对应的x/y/z/sphere添加到intersects数组中
            let all_spheres = [];
            for (let key in g_points3d) {
                let sphere = g_points3d[key].sphere;
                // 为sphere添加属性key,用于后续获取原始的key
                all_spheres.push(sphere);

            }
            // console.log('all_spheres:', all_spheres);
            const intersects = raycaster.intersectObjects(all_spheres);
            console.log('intersects:', intersects);
            if (intersects.length > 0) {
                console.log('射线与物体相交');
                for (let i = 0; i < intersects.length; i++) {
                    let current_sphere = intersects[i].object;
                    // 点击同一个球体，不处理
                    // if (current_sphere === g_last_click_sphere) {
                    //     continue;
                    // }
                    g_last_click_sphere = current_sphere;
                    console.log('current_sphere:', current_sphere);
                    // 获取球体对象的key属性
                    let key = current_sphere.userData.key;
                    console.log('key:', key);
                    console.log('piancha:', current_sphere.userData.piancha);

                    current_sphere.material.color.set(0xff00f0);
                    setTimeout(() => {
                        current_sphere.material.color.set(0xffff00);
                    }, 5000);

                    let show_message = `key: ${key}, x: ${current_sphere.userData.piancha.x}, y: ${current_sphere.userData.piancha.y}, z: ${current_sphere.userData.piancha.z}`
                    showNotify(show_message);
                }
            } else {
                console.log('射线未与任何物体相交');
            }

        })

        // 初始绘制3d点
        function draw_points3d(coorddataobj) {
            // csvData.forEach((item) => (points[item.point] = { x: item.x, y: item.y, z: item.z }))
            // 遍历coorddataobj，将每个特征点的坐标添加到points中
            for (const key in coorddataobj) {
                let item = coorddataobj[key]
                let point_piancha = { x: item.last.x, y: item.last.y, z: item.last.z }
                // console.log('point_piancha:', key, point_piancha)
                let point_weizhi = { x: item.名义值.x, y: item.名义值.y, z: item.名义值.z }
                // console.log('point_weizhi:', point_weizhi)

                g_points3d[key] = {};
                // 2. 准备几何体
                // 使用 THREE.ArrowHelper 可视化向量
                let vector = new THREE.Vector3(point_weizhi.x, point_weizhi.y, point_weizhi.z);

                let x_dir = point_piancha.x > 0 ? 1 : -1;
                let x_len = Math.abs(point_piancha.x)
                let x_arrow = new THREE.ArrowHelper(new THREE.Vector3(x_dir, 0, 0), vector, 50 * x_len, 0xff0000, 10, 10);
                g_points3d[key].x = x_arrow;
                scene.add(g_points3d[key].x);

                let y_dir = point_piancha.y > 0 ? 1 : -1;
                let y_len = Math.abs(point_piancha.y)
                let y_arrow = new THREE.ArrowHelper(new THREE.Vector3(0, y_dir, 0), vector, 50 * y_len, 0x00ff00, 8, 6);
                y_arrow.userData.key = key;

                g_points3d[key].y = y_arrow;
                scene.add(g_points3d[key].y);

                let z_dir = point_piancha.z > 0 ? 1 : -1;
                let z_len = Math.abs(point_piancha.z)
                let z_arrow = new THREE.ArrowHelper(new THREE.Vector3(0, 0, z_dir), vector, 50 * z_len, 0x0000ff, 10, 10);
                g_points3d[key].z = z_arrow;
                scene.add(g_points3d[key].z);

                // 创建球体，直径为 point_piancha 的最大值
                let sphereGeometry = new THREE.SphereGeometry(5 * Math.max(Math.abs(point_piancha.x), Math.abs(point_piancha.y), Math.abs(point_piancha.z)), 32, 32);
                let sphereMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 });
                let sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
                sphere.position.set(point_weizhi.x, point_weizhi.y, point_weizhi.z);
                // 初始时，设置球体隐藏
                sphere.visible = false;
                sphere.userData.key = key;
                sphere.userData.piancha = point_piancha;
                g_points3d[key].sphere = sphere;
                scene.add(g_points3d[key].sphere);

            }

        }

        // 根据坐标数据更新3d点
        function update_points3d_from_coord(coorddataobj) {
            for (const key in coorddataobj) {
                // 如果没有对应的点，跳过
                if (!g_points3d[key]) {
                    continue
                }
                let item = coorddataobj[key]
                let point_piancha = { x: item.last.x, y: item.last.y, z: item.last.z }
                // console.log('point_piancha:', key, point_piancha)
                let point_weizhi = { x: item.名义值.x, y: item.名义值.y, z: item.名义值.z }
                // console.log('point_weizhi:', point_weizhi)

                let x_arrow = g_points3d[key].x;
                let x_dir = point_piancha.x > 0 ? 1 : -1;
                let x_len = Math.abs(point_piancha.x)
                // let x_arrow = new THREE.ArrowHelper(new THREE.Vector3(x_dir, 0, 0), vector, 50 * x_len, 0xff0000, 10, 10);
                // 设置箭头的位置
                // x_arrow.position.set(point_weizhi.x, point_weizhi.y, point_weizhi.z);
                // 更新箭头的方向和长度
                x_arrow.setDirection(new THREE.Vector3(x_dir, 0, 0));
                x_arrow.setLength(50 * x_len);

                g_points3d[key].x = x_arrow;
                // scene.add(points3d[key].x);
                let y_arrow = g_points3d[key].y;
                let y_dir = point_piancha.y > 0 ? 1 : -1;
                let y_len = Math.abs(point_piancha.y)
                // let y_arrow = new THREE.ArrowHelper(new THREE.Vector3(0, y_dir, 0), vector, 50 * y_len, 0x00ff00, 8, 6);
                // 设置箭头的位置
                // y_arrow.position.set(point_weizhi.x, point_weizhi.y, point_weizhi.z);
                // 更新箭头的方向和长度
                y_arrow.setDirection(new THREE.Vector3(0, y_dir, 0));
                y_arrow.setLength(50 * y_len);

                g_points3d[key].y = y_arrow;
                // scene.add(points3d[key].y);

                let z_arrow = g_points3d[key].z;
                let z_dir = point_piancha.z > 0 ? 1 : -1;
                let z_len = Math.abs(point_piancha.z)
                // let z_arrow = new THREE.ArrowHelper(new THREE.Vector3(0, 0, z_dir), vector, 50 * z_len, 0x0000ff, 10, 10);
                // 设置箭头的位置
                // z_arrow.position.set(point_weizhi.x, point_weizhi.y, point_weizhi.z);
                // 更新箭头的方向和长度
                z_arrow.setDirection(new THREE.Vector3(0, 0, z_dir));
                z_arrow.setLength(50 * z_len);

                // points3d[key].z = z_arrow;
                // scene.add(points3d[key].z);

                let sphere = g_points3d[key].sphere;
                // 创建球体，直径为 point_piancha 的最大值
                // let sphereGeometry = new THREE.SphereGeometry(5 * Math.max(Math.abs(point_piancha.x), Math.abs(point_piancha.y), Math.abs(point_piancha.z)), 32, 32);
                // let sphereMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 });
                // let sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
                // sphere.position.set(point_weizhi.x, point_weizhi.y, point_weizhi.z);
                // 更新球体的半径
                sphere.diameter = 5 * Math.max(Math.abs(point_piancha.x), Math.abs(point_piancha.y), Math.abs(point_piancha.z));
                sphere.geometry.radius = sphere.diameter / 2;

                g_points3d[key].sphere = sphere;
                // scene.add(points3d[key].sphere);

            }

        }

        // 根据last_i 更新3d点
        async function update_points3d(last_i) {
            // 遍历coorddataobj，将每个特征点的坐标添加到points中
            // let last_i = 'last_' + i

            g_coorddataobj = await request_coorddata(last_i)
            console.log('last_i:', last_i)
            // console.log('coorddataobj:', coorddataobj)

            update_points3d_from_coord(g_coorddataobj)

        }
        // 显示弹窗函数
        function showNotify(message, duration = 3000) {
            const toast = document.getElementById('notifyToast');
            // 设置toast的位置为当前鼠标点击位置
            toast.style.left = event.clientX + 'px';
            toast.style.top = event.clientY + 'px';

            console.log('message:', message);
            toast.textContent = message;
            toast.style.display = 'block';
            // 自动关闭
            setTimeout(() => {
                toast.style.display = 'none';
            }, duration);
        }

        // 为两个按钮添加点击事件处理函数
        document.getElementById('btnDefaultTarget').addEventListener('click', function () {
            // 设置target为默认位置(1800, 0, 600)
            controls.target.set(1800, 0, 600);
            // 如果需要，可以同时调整相机位置
            camera.position.set(0, -4000, 0);
            // camera.lookAt(1800, 0, 600);
            // 触发渲染更新
            // renderer.render(scene, camera);
            showNotify('已将目标设置为默认位置 (1800, 0, 600)');
        });

        document.getElementById('btnLastClickTarget').addEventListener('click', function () {
            if (g_last_click_sphere !== null) {
                // 获取球体的世界坐标
                const sphereWorldPosition = new THREE.Vector3();
                g_last_click_sphere.getWorldPosition(sphereWorldPosition);

                // 设置target为球体的位置
                controls.target.copy(sphereWorldPosition);

                // 可以调整相机位置以更好地查看目标点
                // 计算相机应该放置的位置（从当前视角方向向后移动一定距离）
                const cameraPosition = new THREE.Vector3();
                camera.getWorldPosition(cameraPosition);
                const direction = new THREE.Vector3().subVectors(cameraPosition, sphereWorldPosition).normalize();
                const newCameraPosition = new THREE.Vector3().addVectors(sphereWorldPosition, direction.multiplyScalar(1000));
                camera.position.copy(newCameraPosition);
                camera.lookAt(sphereWorldPosition);

                showNotify(`已将目标设置为上次点击位置: (${sphereWorldPosition.x.toFixed(2)}, ${sphereWorldPosition.y.toFixed(2)}, ${sphereWorldPosition.z.toFixed(2)})`);
            } else {
                showNotify('请先点击场景中的球体');
            }
        });
        // 获取元素
        const helperDiv = document.querySelector('.helper');
        const toggleHelperBtn = document.getElementById('toggleHelperBtn');
        const helperCloseBtn = document.getElementById('helperCloseBtn');

        // 初始状态控制
        let isHelperVisible = true; // 默认显示

        // 当页面加载完成后，显示helper和内部的关闭按钮，隐藏外部的控制按钮
        toggleHelperBtn.style.display = 'none';

        // helper内部关闭按钮的点击事件
        helperCloseBtn.addEventListener('click', function () {
            helperDiv.style.display = 'none';
            toggleHelperBtn.textContent = '显示说明';
            toggleHelperBtn.style.display = 'block';
            isHelperVisible = false;
        });

        // 外部控制按钮的点击事件
        toggleHelperBtn.addEventListener('click', function () {
            if (!isHelperVisible) {
                helperDiv.style.display = 'block';
                toggleHelperBtn.style.display = 'none';
                isHelperVisible = true;
            }
        });

    </script>
</body>

</html>